package com.chat.api.utils.util;

import java.io.UnsupportedEncodingException;

/**
 * An implementation of the XTEA block cipher algorithm.
 * <p>
 * This implementation uses 32 rounds.
 * The best attack reported as of 2009 is 36 rounds (Wikipedia).
 */
public class XTEAUtil {


   /**
   * Encodes and decodes .
   */

	 public static String encrypt(String username, String clearpassword)
	 {
	 	
	 			 try
	 			 {	 	
			 			if (username== null || clearpassword == null) return null;
				 		byte[] keyBytes = username.getBytes("ISO-8859-1");
				 		byte[] passBytes = clearpassword.getBytes("ISO-8859-1"); 			 		
				 		XTEAUtil mytea = new XTEAUtil(keyBytes, 80);
				 		byte[] encrypted = mytea.encode(passBytes, passBytes.length);
						return(base64Encode(encrypted));
			 	} 
			 	catch (Exception ex)
			 	{
	 					return(null);
				}	 				
	 } 

	 public static String decrypt(String username, String encrypted)
	 {
	 		try
	 		{	 	
				 	byte[] keybytes = username.getBytes("ISO-8859-1");
				 	XTEAUtil mytea = new XTEAUtil(keybytes, 80);
				 	byte[] encryptedBytes = base64Decode(encrypted);
	 				byte[] decrpted = mytea.decode(encryptedBytes,encryptedBytes.length);
	 				return (new String(decrpted, "ISO-8859-1")); 
	 		} catch (Exception ex)
	 		{
	 			return(null);
	 		}
				 		
	 } 

    private static final int DELTA = 0x9E3779B9;
    private int k0, k1, k2, k3, k4, k5, k6, k7;
    private int k8, k9, k10, k11, k12, k13, k14, k15;
    private int k16, k17, k18, k19, k20, k21, k22, k23;
    private int k24, k25, k26, k27, k28, k29, k30, k31;

   /**
   * Accepts key for enciphering/deciphering.
   *
   * @throws ArrayIndexOutOfBoundsException if the key isn't the correct length.
   *
   * @param key 128 bit (16 byte) key.
   */
   public XTEAUtil(byte origin_key[], int paddingByte)
   {
      int klen = origin_key.length;
      byte[] key = new byte[16];
      int j, i;
      System.arraycopy(origin_key, 0, key, 0, klen < 16? klen:16);
      if (klen < 16)
      {
      		for (i = klen; i < 16; i ++)  key[i] = (byte)(paddingByte & 0xFF);    
      }
			setKey(key);            
   }

    
    public void setKey(byte[] b) {
    	
        int[] key = new int[4];
        
         key[0] = (b[0] << 24) + ((b[1] & 255) << 16) + ((b[2] & 255) << 8) + (b[3] & 255);
			   key[1] = (b[4] << 24) + ((b[5] & 255) << 16) + ((b[6] & 255) << 8) + (b[7] & 255);
         key[2] = (b[8] << 24) + ((b[9] & 255) << 16) + ((b[10] & 255) << 8) + (b[11] & 255);
         key[3] = (b[12] << 24) + ((b[13] & 255) << 16) + ((b[14] & 255) << 8) + (b[15] & 255);

				
        int[] r = new int[32];
        for (int i = 0, sum = 0; i < 32;) {
            r[i++] = sum + key[sum & 3];
            sum += DELTA;
            r[i++] = sum + key[ (sum >>> 11) & 3];
        }
        k0 = r[0]; k1 = r[1]; k2 = r[2]; k3 = r[3];
        k4 = r[4]; k5 = r[5]; k6 = r[6]; k7 = r[7];
        k8 = r[8]; k9 = r[9]; k10 = r[10]; k11 = r[11];
        k12 = r[12]; k13 = r[13]; k14 = r[14]; k15 = r[15];
        k16 = r[16]; k17 = r[17]; k18 = r[18]; k19 = r[19];
        k20 = r[20]; k21 = r[21]; k22 = r[22]; k23 = r[23];
        k24 = r[24]; k25 = r[25]; k26 = r[26]; k27 = r[27];
        k28 = r[28]; k29 = r[29]; k30 = r[30]; k31 = r[31];
    }

    private void encryptBlock(byte[] in, byte[] out, int off) {
        int y = (in[off] << 24) | ((in[off + 1] & 255) << 16)
                | ((in[off + 2] & 255) << 8) | (in[off + 3] & 255);
        int z = (in[off + 4] << 24) | ((in[off + 5] & 255) << 16)
                | ((in[off + 6] & 255) << 8) | (in[off + 7] & 255);
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k0;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k1;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k2;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k3;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k4;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k5;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k6;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k7;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k8;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k9;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k10;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k11;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k12;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k13;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k14;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k15;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k16;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k17;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k18;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k19;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k20;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k21;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k22;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k23;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k24;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k25;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k26;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k27;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k28;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k29;
        y += (((z << 4) ^ (z >>> 5)) + z) ^ k30;
        z += (((y >>> 5) ^ (y << 4)) + y) ^ k31;
        out[off] = (byte) (y >> 24);
        out[off + 1] = (byte) (y >> 16);
        out[off + 2] = (byte) (y >> 8);
        out[off + 3] = (byte) y;
        out[off + 4] = (byte) (z >> 24);
        out[off + 5] = (byte) (z >> 16);
        out[off + 6] = (byte) (z >> 8);
        out[off + 7] = (byte) z;
    }

    private void decryptBlock(byte[] in, byte[] out, int off) {
        int y = (in[off] << 24) | ((in[off + 1] & 255) << 16)
                | ((in[off + 2] & 255) << 8) | (in[off + 3] & 255);
        int z = (in[off + 4] << 24) | ((in[off + 5] & 255) << 16)
                | ((in[off + 6] & 255) << 8) | (in[off + 7] & 255);
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k31;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k30;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k29;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k28;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k27;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k26;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k25;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k24;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k23;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k22;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k21;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k20;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k19;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k18;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k17;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k16;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k15;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k14;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k13;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k12;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k11;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k10;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k9;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k8;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k7;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k6;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k5;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k4;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k3;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k2;
        z -= (((y >>> 5) ^ (y << 4)) + y) ^ k1;
        y -= (((z << 4) ^ (z >>> 5)) + z) ^ k0;
        out[off] = (byte) (y >> 24);
        out[off + 1] = (byte) (y >> 16);
        out[off + 2] = (byte) (y >> 8);
        out[off + 3] = (byte) y;
        out[off + 4] = (byte) (z >> 24);
        out[off + 5] = (byte) (z >> 16);
        out[off + 6] = (byte) (z >> 8);
        out[off + 7] = (byte) z;
    }

    
   /**
   * Byte wrapper for encoding.
   * Converts bytes to ints.
   * Padding will be added if required.
   *
   * @param b incoming <code>byte</code> array
   *
   * @param byte length
   *
   * @return int conversion array, possibly with padding.
   *
   * @see #padding
   */
   
   public byte [] encode(byte b[], int length)
   {
      int j;
      
      byte bp[] = new byte[(length/8+1)*8];
      int bLen = bp.length;
      
      System.arraycopy(b, 0, bp, 0, length);
            
			bp[bLen - 1] = (byte)((length % 8) &0xFF) ; // the last is remain length after N 8-byte blocks    

			byte out[] = new byte[bLen];
			
      for (j = 0; j < bLen; j += 8)
      {
				    encryptBlock(bp, out, j);     
      }
      return out;
   }
   
   /**
   * Convert a byte array to ints and then decode.
   * There may be some padding at the end of the byte array from
   * the previous encode operation.
   *
   * @param b bytes to decode
   * @param count number of bytes in the array to decode
   *
   * @return <code>byte</code> array of decoded bytes.
   */
   public byte [] decode(byte b[], int count)
   {
      int intCount = count / 4;
      if (intCount <  2 ) return null;
      byte[] bp = new byte[intCount * 4];
      for (int j = 0; j < bp.length; j += 8)
      {
      	decryptBlock(b, bp, j);
      }
      int length = bp.length;      
      int padding = bp[length - 1];
      if (padding > 8 ) return null;
      length = length - 8 + padding;
      byte[] outBytes = new byte[length];
      System.arraycopy(bp, 0, outBytes, 0, length);
      return(outBytes);        
   }

	 
 		private static char[] base64EncodeChars = new char[] { 
         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
         'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
         'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 
         'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
         'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
         'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
         'w', 'x', 'y', 'z', '0', '1', '2', '3', 
         '4', '5', '6', '7', '8', '9', '+', '/' }; 
            
   private static byte[] base64DecodeChars = new byte[] { 
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 
     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 
     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 
     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 
     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 }; 
   
   private static String base64Encode(byte[] data) { 
         StringBuffer sb = new StringBuffer(); 
         int len = data.length; 
         int i = 0; 
         int b1, b2, b3; 
         while (i < len) { 
             b1 = data[i++] & 0xff; 
             if (i == len) 
             { 
                 sb.append(base64EncodeChars[b1 >>> 2]); 
                 sb.append(base64EncodeChars[(b1 & 0x3) << 4]); 
                 sb.append("=="); 
                 break; 
             } 
             b2 = data[i++] & 0xff; 
             if (i == len) 
             { 
                 sb.append(base64EncodeChars[b1 >>> 2]); 
                 sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); 
                 sb.append(base64EncodeChars[(b2 & 0x0f) << 2]); 
                 sb.append("="); 
                 break; 
             } 
             b3 = data[i++] & 0xff; 
             sb.append(base64EncodeChars[b1 >>> 2]); 
             sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); 
             sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]); 
             sb.append(base64EncodeChars[b3 & 0x3f]); 
         } 
         return sb.toString(); 
     } 
   
  private static byte[] base64Decode(String str) throws UnsupportedEncodingException { 
         StringBuffer sb = new StringBuffer(); 
         byte[] data = str.getBytes("US-ASCII"); 
         int len = data.length; 
         int i = 0; 
         int b1, b2, b3, b4; 
         while (i < len) { 
             /* b1 */ 
             do { 
                 b1 = base64DecodeChars[data[i++]]; 
             } while (i < len && b1 == -1); 
             if (b1 == -1) break; 
             /* b2 */ 
             do { 
                 b2 = base64DecodeChars[data[i++]]; 
             } while (i < len && b2 == -1); 
             if (b2 == -1) break; 
             sb.append((char)((b1 << 2) | ((b2 & 0x30) >>> 4))); 
             /* b3 */ 
             do { 
                 b3 = data[i++]; 
                 if (b3 == 61) return sb.toString().getBytes("ISO-8859-1"); 
                 b3 = base64DecodeChars[b3]; 
             } while (i < len && b3 == -1); 
             if (b3 == -1) break; 
             sb.append((char)(((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2))); 
             /* b4 */ 
             do { 
                 b4 = data[i++]; 
                 if (b4 == 61) return sb.toString().getBytes("ISO-8859-1"); 
                 b4 = base64DecodeChars[b4]; 
             } while (i < len && b4 == -1); 
             if (b4 == -1) break; 
             sb.append((char)(((b3 & 0x03) << 6) | b4)); 
         } 
         return sb.toString().getBytes("ISO-8859-1"); 
     } 
	 
	   // Display some bytes in HEX.
   //
   public String dumpBytes(byte b[])
   {
      StringBuffer r = new StringBuffer();
      final String hex = "0123456789ABCDEF";

      for (int i = 0; i < b.length; i++)
      {
      	 r.append('0');r.append('x');
         int c = ((b[i]) >>> 4) & 0xf;
         r.append(hex.charAt(c));
         c = ((int)b[i] & 0xf);
         r.append(hex.charAt(c));
         r.append(',');
      }

      return r.toString();
   }
    
   public static void main(String args[])
   {
      /*if (args.length == 3)
      {
      	 if (args[0].equals("encode")) 
      	 {
      	 	     	 		
    		  	System.out.println("encrpted=" + XTEA.encrypt(args[1], args[2]));  
      	 }
      	 else
      	 {
      	 	 
    		  	System.out.println("decrpted=" + XTEA.decrypt(args[1], args[2]));  
      	 	
      	 } 
    	}
			else System.out.println("Usage: xtea encode/decode username password/encrpted");
			*/
	   String user = "zjp@myzjcloud.com";
	   String pwd = "123456";
	   String encrp = "SlfCxkojnio=";
	   System.out.println(  XTEAUtil.encrypt(user, pwd) );
	   System.out.println(  XTEAUtil.decrypt(user, encrp) );
   }

}
